package com.remoter.cluster.internal;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.remoter.api.cluster.support.AbstractLoadBalance;
import com.remoter.api.context.support.Exporter;
import com.remoter.api.context.support.Importer;
import com.remoter.api.extension.annotation.ExtensionName;
import com.remoter.api.util.AtomicPositiveInteger;

@ExtensionName("roundrobin")
public class RoundRobinLoadBalance extends AbstractLoadBalance{

	private final ConcurrentMap<String,AtomicPositiveInteger> sequences = new ConcurrentHashMap<String,AtomicPositiveInteger>();
	
	private static final class IntegerWrapper {
		public IntegerWrapper(int value) {
			this.value = value;
		}
		private int value;
		public int getValue() {
			return value;
		}
		public void decrement() {
			this.value--;
		}
	}
	
	@Override
	public Exporter doSelect(Importer importer,List<Exporter> exporters) {
		String key = importer.getServiceKey();
		int length = exporters.size(); // 总个数
		int maxWeight = 0; // 最大权重
		int minWeight = Integer.MAX_VALUE; // 最小权重
		final LinkedHashMap<Exporter,IntegerWrapper> invokerToWeightMap = new LinkedHashMap<Exporter,IntegerWrapper>();
		int weightSum = 0;
		for(int i=0;i<length;i++){
			int weight = getWeight(exporters.get(i));
			maxWeight = Math.max(maxWeight, weight); // 累计最大权重
			minWeight = Math.min(minWeight, weight); // 累计最小权重
			if(weight > 0){
				invokerToWeightMap.put(exporters.get(i),new IntegerWrapper(weight));
				weightSum += weight;
			}
		}
		AtomicPositiveInteger sequence = sequences.get(key);
		if(null == sequence){
			sequences.putIfAbsent(key,new AtomicPositiveInteger());
			sequence = sequences.get(key);
		}
		int currentSequence = sequence.getAndIncrement();
		if(maxWeight>0 && minWeight<maxWeight){ // 权重不一样
			int mod = currentSequence % weightSum;
			for(int i=0; i<maxWeight;i++){
				for(Entry<Exporter,IntegerWrapper> each : invokerToWeightMap.entrySet()){
					final Exporter k = each.getKey();
					final IntegerWrapper v = each.getValue();
					if(mod == 0 && v.getValue()>0){
						return k;
					}
					if(v.getValue() > 0){
						v.decrement();
						mod--;
					}
				}
			}
		}
		return exporters.get(currentSequence % length);
	}
	
}